<?php

/*
 * Abstrace Database Class
 * 
 * 数据库类
 * 
 */
abstract class DB {

    const DB_QUERY_REGEXP = '/(%d|%s|%%|%f|%b|%n)/';
    
    protected $conn = false;
    protected $opened = false;
    protected $prefix = '';
    protected $charset = 'utf8';
    protected $collat = 'utf8_general_ci';
    protected $debug = false;
    protected $pconnect = false;
    
    
    protected $result;
    protected $lastQuery = '';

    /*
     * @param array $p      配置信息
      array(
      'dbdriver' => 'mysql',
      'hostname' => 'localhost',
      'username' => 'root',
      'password' => '',
      'database' => 'ic',
      'db_debug' => true,
      'char_set' => 'utf8',
      'dbcollat' => 'utf8_general_ci',
      'dbprefix' => '',
      'pconnect' => false,
      );
     */

    protected function __construct($p) {

        if (isset($p['dbprefix'])) {
            $this->prefix = $p['dbprefix'];
        }
        if (isset($p['charset'])) {
            $this->charset = $p['charset'];
        }
        if (isset($p['dbcollat'])) {
            $this->collat = $p['dbcollat'];
        }
        if (isset($p['pconnect'])) {
            $this->pconnect = $p['pconnect'];
        }
        if (isset($p['db_debug'])) {
            $this->debug = $p['db_debug'];
        }

        $this->_connect($p['hostname'], $p['username'], $p['password'], $p['database']);
    }

    public final static function factory($dbType, $p = array()) {

        $db_type = strtolower($dbType);

        $class = 'DB_' . ucfirst($db_type);

        require_once $class . '.php';

        if (class_exists($class)) {
            return new $class($p);
        } else {
            return null;
        }
    }

    public function isOpen() {
        return $this->opened;
    }
    
    public function close(){
        $this->opened = false;
        
        if($this->conn){
            $ret = $this->_closeConnect();
            $this->conn = false;
            
            return $ret;
        }
        
        return true;
    }
    
    public function getLastQuery(){
        return $this->lastQuery;
    }

    protected function prefixTable($sql) {
        return strtr($sql, array('{' => $this->prefix, '}' => ''));
    }

    protected function _errorPage($msg) {
        echo $msg;
        exit();
    }

    public function query($query) {
        $args = func_get_args();
        
        array_shift($args);
        
        $query = $this->prefixTable($query);
        
        if(isset($args[0]) and is_array($args[0])){
            $args = $args[0];
        }
        
        $this->_query_callback($args, TRUE);
        $query = preg_replace_callback(self::DB_QUERY_REGEXP, array($this,'_query_callback'), $query);
        
        $this->result = $this->_query($query);
        
        return $this->result;
    }

    protected function _query_callback($match, $init = false) {
        static $args = NULL;

        if ($init) {
            $args = $match;
            return;
        }

        switch ($match[1]) {
            case '%d':
                return (int) array_shift($args);

            case '%s':
                return $this->_escape_string(array_shift($args));

            case '%n':
                // Numeric values have arbitrary precision, so can't be treated as float.
                // is_numeric() allows hex values (0xFF), but they are not valid.
                $value = trim(array_shift($args));
                return is_numeric($value) && !preg_match('/x/i', $value) ? $value : '0';

            case '%%':
                return '%';
            case '%f':
                return (float) array_shift($args);

            case '%b':
                return _encode_blob(array_shift($args));
        }
    }

    public abstract function getType();

    protected abstract function _query($query);

    protected abstract function _connect($server, $user, $pswd, $db);

    protected abstract function _closeConnect();

    protected abstract function _escape_string($str);

    protected abstract function _encode_blob($data);
    
    protected function escapeTable($string){
        return preg_replace('/[^A-Za-z0-9_]+/', '', $string);
    }
    
    public abstract function lockTable($table);
    
    public abstract function unlockTables();
    
    public abstract function tableExists($table);
    
    public abstract function fetchArray($result);
    
    public abstract function fetchObject($result);
    
    public abstract function result($result = null);
    
    public abstract function resultArray($result = null);

    public abstract function resultObject($result = null);
    
    public abstract function affectedRows();
    
    public abstract function insertId();
}
